package com.jbm.core.common;

import com.jbm.core.client.MainCallback;
import com.jbm.core.common.ResponseRecvCallback.ResponseRecvCallback;
import com.jbm.core.iface.IAcross;
import com.jbm.core.iface.IConnectAgent;
import com.jbm.core.pb.PBIAcross;
import com.jbm.core.server.AccountCallback;
import com.jbm.core.server.SAccount;

import org.apache.mina.core.future.WriteFuture;
import org.apache.mina.core.session.IoSession;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;

/**
 * Created by root on 17-2-27.
 */
public class AutoCall
{
	public final static AutoCall g_autocall = new AutoCall();

	protected String[] m_iface_classes;
	protected String[] m_call_classes;
	protected String[] m_client_call_classes;

	protected HashMap<String,FaceMap> m_all_classes;

	protected Resource m_dialogqueue = null;
	protected HashMap<String,Integer> m_forcecall;

	protected final Object m_sa_mutex = new Object();
	protected final Object m_ca_mutex = new Object();

	public AutoCall()
	{
	}

	public String[] getM_iface_classes()
	{
		return m_iface_classes;
	}

	public String[] getM_call_classes()
	{
		return m_call_classes;
	}

	public String[] getM_client_call_classes()
	{
		return m_client_call_classes;
	}

	public HashMap<String,FaceMap> getM_all_classes()
	{
		return m_all_classes;
	}

	public void InitAC ( int rescnt, String forcecall, String iface_classes, String client_call_classes, String server_call_classes )
	{
		m_dialogqueue = new Resource(rescnt,"dialog");

		if ( forcecall != null )
		{
			m_forcecall = new HashMap<String, Integer>();

			String[] mf = forcecall.split("\\|");
			for (int i = 0; i < mf.length; i++)
				m_forcecall.put(mf[i], 1);
		}

		if ( iface_classes == null )
			iface_classes = "com.jbm.core";
/*
		if ( server_call_classes == null )
			server_call_classes = "com.jbm.core.server";
		if ( client_call_classes == null )
			client_call_classes = "com.jbm.core.client";
*/

		m_iface_classes = iface_classes.split("\\|");

		if( server_call_classes != null )
			m_call_classes = server_call_classes.split("\\|");
		else if ( client_call_classes != null )
			m_call_classes = client_call_classes.split("\\|");
		else
			m_call_classes = "com.jbm.core.server".split("\\|");

		if ( client_call_classes != null )
			m_client_call_classes = client_call_classes.split("\\|");
		else
			m_client_call_classes = "com.jbm.core.client".split("\\|");

		m_all_classes = new HashMap<String,FaceMap>();

		List<String> allclasses = null;

		MainCallback callback = MainServer.getG_server().getM_maincallback();

		if ( callback != null )
		{
			allclasses = callback.getAllIFace();
		}

		if ( allclasses == null )
			for ( String classpackage:m_iface_classes )
			{
				List<String> classes = PackageUtil.getClassName(classpackage+".iface",false);

				for ( String ifaceclass:classes)
				{
					FaceMap fmap = new FaceMap(ifaceclass,classpackage);

					m_all_classes.put(fmap.m_simplename,fmap);
					SysLog.g_syslog.MakeLog(SysLog.SYS_LOG_LEVEL_DEBUG,"DO....%d",m_all_classes.size());
				}
			}
		else
			for ( String ifaceclass:allclasses)
			{
				FaceMap fmap = new FaceMap(ifaceclass,ifaceclass.substring(0,ifaceclass.lastIndexOf(".iface")));

				m_all_classes.put(fmap.m_simplename,fmap);
			}
	}

	public P2PDialog GetDialog (PBIAcross.IAcross iacross)
	{
		return (P2PDialog)m_dialogqueue.Get(new ResKey(iacross.getQuery()),iacross);
	}

	public boolean Call (PBIAcross.IAcross req, ResponseRecvCallback rrcallback, boolean isfirst, int tarid, String tartag, char server_type )
	{
		if ( isfirst )
			req = MSGQueue3MongoDB.take(req);

		if ( tarid == 0 )
			if ( ( req.getRandr() & RandR.RANDR_TYPE_RESPONSE ) == RandR.RANDR_TYPE_RESPONSE )
			{
//				if (req.hasFromid())
				if (req.getFromid()!=0)
				{
					tarid = req.getFromid();
					tartag = req.getFromtag();
				}
			}else
			{
//				if (req.hasToid())
				if (req.getToid()!=0)
				{
					tarid = req.getToid();
					tartag = req.getTotag();
				}
			}

		ConnectAgent[] cas = null;
		if ( MainServer.getG_server().getM_server_type() == MainServer.Server_type_Server )
		{
			SAccount sa = AccountCtrl.g_accountctrl.GetSA(tarid);

			if ( sa != null )
				if ( tartag == null || tartag.equals(""))
					cas = sa.GetCAS();
				else
				{
					cas = new ConnectAgent[1];
					cas[0] = sa.GetCA(tartag);
				}
		}else if ( MainServer.getG_server().getM_server_type() == MainServer.Server_type_Client )
			if ( tartag == null || tartag.equals(""))
				cas = AccountCtrl.g_accountctrl.GetCAS(tarid);
			else
			{
				cas = new ConnectAgent[1];
				cas[0] = AccountCtrl.g_accountctrl.GetCA(tarid, tartag);
			}

		if ( cas == null || cas.length == 0 || cas[0] == null )
		{
//			MessageQueue.g_mq.Add(req);
			MSGQueue3MongoDB.put(req,false);
			return false;
		}

		for ( ConnectAgent ca:cas )
		{
			if ( (req.getExcode()&IAcross.excode_intask)!=0 )
			{
				P2PDialog p2pdialog = new P2PDialog(10);
				p2pdialog.Born(6, 5000);
				p2pdialog.Bind_IoSession(ca.getM_iosessionShell(MainServer.getG_server().getM_server_type()));
				p2pdialog.Bind_RRCallback(rrcallback);
				p2pdialog.Bind_Isfirst(isfirst);

				m_dialogqueue.Add(p2pdialog);

				p2pdialog.Bind_Exinfo(req);
				PBIAcross.IAcross.Builder builder = req.toBuilder();
				if ((req.getRandr() & RandR.RANDR_TYPE_RESPONSE) == RandR.RANDR_TYPE_RESPONSE)
					builder.setFromtag(ca.getM_app_type());
				else
				{
					builder.setTotag(ca.getM_app_type());
					builder.setQindex(ca.GetQIndex());
				}

				builder.setQuery(p2pdialog.GetLifeName().toString());
				builder.setRandr(builder.getRandr() | RandR.RANDR_TYPE_TASK);
				req = builder.build();
				p2pdialog.Bind_Request(new PBRandR(req));
			}

			ca.Send(req, ConnectAgent.Send_Type_Any,server_type);
		}

		return true;
	}

	public IAcross Call (IoSessionShell iosession,
						 String classname, String method,
						 IAcross content, int fromid, int toid, String fromtag, String totag, int index,
						 boolean hasresp, boolean waitresp, boolean excode, boolean intask, ResponseRecvCallback rrcallback,
						 int trytimes, int timeout)
	{
		IAcross response = null;

		P2PDialog p2pdialog = new P2PDialog(10);
		p2pdialog.Born(trytimes, timeout);
		p2pdialog.Bind_IoSession(iosession);
		p2pdialog.Bind_RRCallback(rrcallback);

		m_dialogqueue.Add(p2pdialog);

		PBIAcross.IAcross iacross = new PBRandR().BuildRequest(
				p2pdialog.GetLifeName().toString(),
				classname,
				method,
				fromid,
				toid,
				fromtag,
				totag,
				index,
				excode,
				intask,
				content );

//		if ( !iacross.hasCode() )
		if ( iacross.getCode() == 0 )
		{
			p2pdialog.Bind_Request(new PBRandR(iacross));
			p2pdialog.setM_content((IAcross)content);

			iosession.Swrite(iacross);

/*
			IoBuffer iobuf = IoBuffer.allocate(100).setAutoExpand(true);
			iobuf.put(iacross.toByteArray());
			iobuf.flip();

			iosession.write(iobuf);
*/

			if (!hasresp)
				p2pdialog.ReleaseFromPool();
			else if (waitresp)
			{
				PBIAcross.IAcross respiacross = (PBIAcross.IAcross) p2pdialog.Get();

				response = new TransClass().PB2_0toI2_0("Client:", respiacross);
			}
		}else
		{
			response = new IAcross(iacross.getCode(),iacross.getInfo());
			p2pdialog.ReleaseFromPool();
		}

//		response.method=method;
		return response;
	}

	public boolean Relay (IoSessionShell iosession, PBIAcross.IAcross pbrequest, int local_id )
	{
		if ((pbrequest.getRandr()&RandR.RANDR_TYPE_TASK)==RandR.RANDR_TYPE_TASK)
			return false;

		int tarid = 0;
		String tartag = null, srctag = null;
		IAccount ia = null;

		if ( ( pbrequest.getRandr() & RandR.RANDR_TYPE_RESPONSE ) == RandR.RANDR_TYPE_RESPONSE )
		{
//			if (pbrequest.hasFromid())
			if (pbrequest.getFromid()!=0)
			{
				tarid = pbrequest.getFromid();
				tartag = pbrequest.getFromtag();
			}

//			if (pbrequest.hasToid()&&(pbrequest.getExcode()&IAcross.excode_intask)!=0)
			if (pbrequest.getToid()!=0&&(pbrequest.getExcode()&IAcross.excode_intask)!=0)
			{
				if (MainServer.getG_server().getM_server_type() == MainServer.Server_type_Server)
					ia = AccountCtrl.g_accountctrl.GetSA(pbrequest.getToid());
				else if (MainServer.getG_server().getM_server_type() == MainServer.Server_type_Client)
					ia = AccountCtrl.g_accountctrl.GetIA(pbrequest.getToid());

				srctag = pbrequest.getTotag();
			}
		}else
		{
//			if (pbrequest.hasToid())
			if (pbrequest.getToid()!=0)
			{
				tarid = pbrequest.getToid();
				tartag = pbrequest.getTotag();
			}

//			if (pbrequest.hasFromid()&&(pbrequest.getExcode()&IAcross.excode_intask)!=0)
			if (pbrequest.getFromid()!=0&&(pbrequest.getExcode()&IAcross.excode_intask)!=0)
			{
				if (MainServer.getG_server().getM_server_type() == MainServer.Server_type_Server)
					ia = AccountCtrl.g_accountctrl.GetSA(pbrequest.getFromid());
				else if (MainServer.getG_server().getM_server_type() == MainServer.Server_type_Client)
					ia = AccountCtrl.g_accountctrl.GetIA(pbrequest.getFromid());

				srctag = pbrequest.getFromtag();
			}
		}

		if ( tarid != 0 && tarid != local_id && local_id != 0 )
		{
			if ( ia != null && !srctag.equals("") )
			{
				ConnectAgent ca = ia.GetCA(srctag);

				if ( ca == null )
					return true;

				if ( !ca.Match_QIndex(pbrequest.getQindex()))
					return true;
			}

			pbrequest = pbrequest.toBuilder().setTimestamp(System.currentTimeMillis()).build();

//			boolean rtn = Call(pbrequest,MessageQueue.g_mq.m_rrcallback,true,tarid,tartag,iosession.getM_server_type());
			boolean rtn = Call(pbrequest,MSGQueue3MongoDB.m_rrcallback,true,tarid,tartag,iosession.getM_server_type());

			if ( !rtn || (pbrequest.getExcode()&IAcross.excode_intask)!=0 )
			{
				PBIAcross.IAcross pbresponse = new PBRandR(pbrequest).BuildResponse(new IAcross(200, "BUFFER"));
				WriteFuture wf = iosession.Swrite(pbresponse);
			}

			return true;
		}

		return false;
	}

	public void UpdatePoint ( IoSessionShell iosession, Integer login_id, String app_type )
	{
		String remotehost = iosession.getRemoteIP();
		int remoteport = iosession.getRemotePort();

		ConnectAgent ca = AccountCtrl.g_accountctrl.GetorAddCA(remotehost+":"+remoteport,null,false);

		UpdatePoint(ca,login_id,app_type);

	}

	public void UpdatePoint (ConnectAgent ca, Integer login_id, String app_type )
	{
		if ( login_id == null || login_id == 0 || ca == null )
			return;

		SAccount sacc = null;

		synchronized (m_sa_mutex)
		{
			sacc = AccountCtrl.g_accountctrl.GetSA(login_id);

			if (sacc == null)
			{
				sacc = new SAccount(login_id);
				sacc.Born(0, 3600000);

				AccountCtrl.g_accountctrl.AddSA(sacc);
				sacc.Start(app_type);
			}
		}

		sacc.Push2Map(ca,new AccountCallback(sacc));
	}

	public void Invoke (IoSessionShell iosession, PBIAcross.IAcross pbrequest, int local_id, String[] call_classes)
	{
		String classname = null;

		Class<?> cls = null;
		IAcross sr = null;
		IAcross response = null;

		ConnectAgent cur_ca = null;

		if ( MainServer.getG_server().getM_server_type() == MainServer.Server_type_Client)
		{
			System.out.println("Client Invoke..........\n"+pbrequest.toString());

			cur_ca = AccountCtrl.g_accountctrl.GetCA(pbrequest.getFromid(),pbrequest.getFromtag());

/*
			if ( cur_ca == null )
				return;

			if (!cur_ca.Match_QIndex(pbrequest.getQindex()))
				return;
*/
		}else
		{
//			System.out.println("Server Invoke..........\n"+pbrequest.toString());

			if ( iosession.getM_iosession() != null )
			{
/*
				ConnectAgent ca = new ConnectAgent(pbrequest.getFromtag(),
						iosession.getRemoteIP(),
						iosession.getRemotePort(),
						iosession.getLocalPort(),
						MainServer.Server_type_Server);

				ca.Born(0, 1800000);
				ca.setM_keepalive(false);
				ca.setM_iosession(iosession.getM_iosession());

				String ca_key = iosession.getRemoteIP() +
						":" + iosession.getRemotePort();

				cur_ca = AccountCtrl.g_accountctrl.GetorAddCA(ca_key, ca, pbrequest.getClass_().equals("1st"));
*/

				ConnectAgent ca = (ConnectAgent)iosession.SgetAttribute("CA_Key");

				if ( ca == null )
				{
					if ( pbrequest.getClass_().equals("1st") )
					{
						ca = new ConnectAgent(pbrequest.getFromtag(),
								iosession.getRemoteIP(),
								iosession.getRemotePort(),
								iosession.getLocalPort(),
								MainServer.Server_type_Server);

						ca.Born(0, 1800000);
						ca.setM_keepalive(false);
						ca.setM_iosession(iosession.getM_iosession());
						AccountCtrl.g_accountctrl.AddCA(ca);
						iosession.SsetAttribute("CA_Key", ca);
					}
				}

				cur_ca = ca;

				int protoc_type = Integer.parseInt((String) iosession.SgetAttribute("PROTOC_TYPE"));
				if (cur_ca == null)
				{
					if (protoc_type == 0 || protoc_type == 0x10) //server 1.0 support
					{
						int isfirst = Integer.parseInt((String) iosession.SgetAttribute("0-1.0_first req"));

						if (isfirst == 1)
						{
							ca = new ConnectAgent(pbrequest.getFromtag(),
									iosession.getRemoteIP(),
									iosession.getRemotePort(),
									iosession.getLocalPort(),
									MainServer.Server_type_Server);

							ca.Born(0, 1800000);
							ca.setM_keepalive(false);
							ca.setM_iosession(iosession.getM_iosession());
							AccountCtrl.g_accountctrl.AddCA(ca);
							iosession.SsetAttribute("CA_Key", cur_ca);
							iosession.SsetAttribute("0-1.0_first req", "0");
						} else
						{
							System.out.println("0-1.0 CA is null");
							return;
						}
					} else
					{
						System.out.println("CA is null");
						return;
					}
				} else
					cur_ca.setM_iosession(iosession.getM_iosession());

				if (protoc_type != 0 && protoc_type != 0x10) //server 1.0 support
					if (!cur_ca.Match_QIndex(pbrequest.getQindex()))
						return;
			}else
			{
				cur_ca = iosession.getM_ca();
				cur_ca.setM_iosession(null);
			}

			if (cur_ca.getM_ca_status() != IConnectAgent.ca_status_authorized &&
					iosession.getM_server_type() == MainServer.Server_type_Server)
				if (m_forcecall.get(pbrequest.getClass_() + "." + pbrequest.getMethod()) == null)
					response = new IAcross(401, "unauthorized");

			if ( response == null )//&&
				if ( pbrequest.getClass_().equals("keepalive"))//||
					response = new IAcross(200, "OK");
				else if ( pbrequest.getClass_().equals("1st"))
					response = new IAcross(200, "OK");
		}

		if ( response == null )
		{
			IAcross request = new TransClass().PB2_0toI2_0("Server:", pbrequest);


			request.m_ca = cur_ca;

			if (request.m_code == 0)
			{
				for (int i = 0; i < call_classes.length; i++)
				{
					classname = call_classes[i] + ".remote.R" + pbrequest.getClass_();

					try
					{
						cls = Class.forName(classname);
					} catch (Exception e)
					{
						sr = new IAcross(404, pbrequest.getClass_() + " not found");
					}

					if (cls != null)
						break;
					else
					{
						classname = call_classes[i] + "." + pbrequest.getClass_();

						try
						{
							cls = Class.forName(classname);
						} catch (Exception e)
						{
							sr = new IAcross(404, pbrequest.getClass_() + " not found");
						}

						if (cls != null)
							break;
					}
				}

				if (cls != null)
				{
					sr = new IAcross(500,"null response");

					Object obj = null;
					Constructor c = null;

					try
					{
						c = cls.getConstructor(IAcross.class);
					} catch (Exception e)
					{
						sr = new IAcross(500, pbrequest.getClass_() + "(IAcross) getConstructor error");
					}

					if (c != null)
					{
						try
						{
							obj = c.newInstance(request);
						} catch (Exception e)
						{
							StackTraceElement[] ste = e.getStackTrace();
							String sste = e.getMessage();

							for(StackTraceElement s:ste)
							{
								sste+= "\r\n";
								sste+= s.toString();
							}
							sr = new IAcross(500, pbrequest.getClass_() + "(IAcross) newInstance failed ("+sste+")");

						}

						if (obj != null)
						{
							Method method = null;

							try
							{
								method = obj.getClass().getMethod(pbrequest.getMethod());
							} catch (Exception e)
							{
								sr = new IAcross(405, pbrequest.getClass_() + "." + pbrequest.getMethod() + "() not found");
							}

							if (method != null)
							{
								try
								{
									response = (IAcross) method.invoke(obj);
								} catch (Exception e)
								{
									StackTraceElement[] ste = e.getStackTrace();
									String sste = e.getMessage();

									for(StackTraceElement s:ste)
									{
										sste+= "\r\n";
										sste+= s.toString();
									}
									sr = new IAcross(500, pbrequest.getClass_() + "." + pbrequest.getMethod() + "() invoke error("+sste+")");
								}
							}
						}
					}
				}

				if (response == null)
					response = sr;
			} else
				response = new IAcross(request.m_code, request.m_info);
		}

		if ( response != null )
		{
			if ( cur_ca != null )
			{
				if ( cur_ca.GetLifeName() != null )
					response.m_ca_key = cur_ca.GetLifeName().toString();

				if (response.m_code <= 200)
					cur_ca.IamLiving();
			}

			PBIAcross.IAcross pbresponse = new PBRandR(pbrequest).BuildResponse((IAcross) response);

			iosession.Swrite(pbresponse);

/*
			IoBuffer iobuf = IoBuffer.allocate(100).setAutoExpand(true);
			iobuf.put(pbresponse.toByteArray());
			iobuf.flip();
			iosession.write(iobuf);
*/
		}
	}

	void IO(IoSession session, ConnectAgent ca, PBIAcross.IAcross message, int local_id, char server_type)
	{
		ConnectAgent lca = null;
		ConnectAgent sca = null;

		int acc_id;

		if ( ca == null && session != null )
			sca = lca = (ConnectAgent) session.getAttribute("CA_Key");
		else if ( ca != null )
			lca = ca;

		if ( lca != null )
		{
			IAccount ia = lca.getM_iaccount();

			if ( ia != null )
				acc_id = ia.getM_account_id();
			else
				acc_id = 0;
		}else
			acc_id = 0;

		if ( sca != null && acc_id != 0 && MainServer.getG_server().getM_server_type() == MainServer.Server_type_Server)
			SysLog.g_syslog.MakeLog(SysLog.SYS_LOG_LEVEL_DEBUG,"%d %s %d %s online %b",acc_id, sca.getM_remotehost(), sca.getM_remoteport(), sca.getM_lifename().toString(),AccountCtrl.g_accountctrl.IsOnline(acc_id));

		PBIAcross.IAcross iacross = message;
//		MainHandler.s_logger.info("Recv:\n" + iacross.toString());

		SysLog.g_syslog.MakeLog(SysLog.SYS_LOG_LEVEL_DEBUG,"%d Recv:\n%s",acc_id, iacross.toString());

		if (AutoCall.g_autocall.Relay(new IoSessionShell(session,ca,server_type), iacross, local_id))
			return;

		if ((iacross.getRandr() & RandR.RANDR_TYPE_RESPONSE) == RandR.RANDR_TYPE_RESPONSE)
		{
			P2PDialog p2pdialog = AutoCall.g_autocall.GetDialog(iacross);
			if (p2pdialog != null)
			{
				MainHandler.s_logger.info("find main dialog...");

				p2pdialog.Put(iacross);
			} else
			{
			}
		}

		if ((iacross.getRandr() & RandR.RANDR_TYPE_REQUEST) == RandR.RANDR_TYPE_REQUEST)
		{
			if ( server_type == MainServer.Server_type_Server )
				AutoCall.g_autocall.Invoke(new IoSessionShell(session,ca,server_type), iacross, local_id, AutoCall.g_autocall.getM_call_classes());
			else
				AutoCall.g_autocall.Invoke(new IoSessionShell(session,ca,server_type), iacross, local_id, AutoCall.g_autocall.getM_client_call_classes());
		}
	}
}